#include "exceptions.h"
#include "field.h"

#include <cstdint>
#include <cstring>
#include <sstream>

// TODO isModified flag() ?


namespace Database
{

using std::size_t;
using std::string;


Field::Field(const ColumnSpec& colspec, const bool isNull, const void *data, const size_t dataLen)
	: colspec_(colspec), isNull_(isNull)
{
	if(isNull_ && !colspec.isNullable())
		throw EFieldValueNull();

	switch(colspec.type())
	{
		case ColumnSpec::Type::INT_UNSIGNED:
			byteLen_ = colspec.maxByteLen();
			data_.dUInt = *(reinterpret_cast<const uint32_t *>(data));
			break;

		case ColumnSpec::Type::VARCHAR:
			byteLen_ = dataLen;
			if(byteLen_ > colspec.maxByteLen())
				throw EFieldValueTooLong();

			// TODO - use refcounted buffer
			data_.dChar = new char[dataLen];
			std::memcpy(data_.dChar, data, byteLen_);
			break;
	}
}


Field::~Field()
{
	// TODO - remove this when refcounted buffer is used for string storage
	switch(colspec_.type())
	{
		case ColumnSpec::Type::VARCHAR:
			delete[] data_.dChar;

		default:
			break;
	}
}


const string Field::asString() const
{
	std::ostringstream os;

	switch(colspec_.type())
	{
		case ColumnSpec::Type::INT_UNSIGNED:
			os << data_.dUInt;
			break;

		case ColumnSpec::Type::VARCHAR:
			os.write(data_.dChar, byteLen_);
			break;
	}

	return os.str();
}


const size_t Field::byteLength() const
{
	return byteLen_;
}


const void * Field::data() const
{
	return &data_;
}


const bool Field::isNull() const
{
	return isNull_;
}


const bool Field::isNullable() const
{
	return colspec_.isNullable();
}


const size_t Field::maxByteLength() const
{
	return colspec_.maxByteLen();
}

}	// namespace Database

